/*----------------------------------------------------------------------------
 * Name:    Timer_1234.c
 * Purpose: Configuration de timers.
 *
 * Version: V1.2
 * Auteurs: P. ACCO (INSA - Toulouse,France)
 *          S. DI MERCURIO (INSA - Toulouse,France)
 *----------------------------------------------------------------------------
 * Ce programme fait partie des pilotes pour STM32 de l'INSA de Toulouse, France.
 * Merci de consulter le fichier LICENCE.txt au sujet des conditions de redistribution,
 * modification et copyright concernant ce pilote. Le fichier LICENCE.txt doit faire partie
 * des fichiers fournis avec ce pilote.
 *
 * Copyright (c) 2012, INSA-GEI Toulouse, France.
 * Tout droits réservés.
 *---------------------------------------------------------------------------*/

#include "stm32f10x.h"
#include "../clock/clock.h"
#include "Timer_1234.h"
#include "../pilotes/gpio/gpio.h"

extern void mise_a_1(uint16_t * registre, int bit);

void (*callbackTimers[8])(void);

void (* get_Callback_Timer(int numero_timer))()
{
    return callbackTimers[numero_timer - 1];
}

void set_Callback_Timer(int numero_timer, void (*function) (void))
{
    callbackTimers[numero_timer - 1] = function;
}


/**
* Configure les Timers 1, 2, 3 et 4
* @param Timer Pointeur vers le jeu de registres (de type TIM_TypeDef) du timer considéré
* @param Duree_us Intervalle de temps exprimé en μs entre deux débordements successifs
* @return Le durée véritable qui a été configurée
**/
float Timer_1234_Init(TIM_TypeDef *Timer, float Duree_us )
{
	// activation du timer
	if (Timer == TIM2)
	  RCC->APB1ENR = RCC->APB1ENR | 0x00000001 ;
	if (Timer == TIM3)
		RCC->APB1ENR = RCC->APB1ENR | 0x00000002 ;
	if (Timer == TIM4)
		RCC->APB1ENR = RCC->APB1ENR | 0x00000004 ;
	if (Timer == TIM1) 
		RCC->APB2ENR = RCC->APB2ENR | 0x00000800 ;
	
  // activation du CNE
	Timer->CR1 |= 0x0001 ;
	
	// On part d'un ARR = 0xFFFF pour déterminer le prescaler
  uint16_t temp_ARR = 0xFFFF;
	
	float frequence = 1000000/Duree_us;
	
	float frequence_interne = CLOCK_GetTIMCLK(Timer);
	
	// On calcule le PSC (avec la formule : Fréquence(Timer) = F(Clock)/ ( (PSC + 1)*(ARR +1) ))
	Timer->PSC = (frequence_interne / (frequence * (temp_ARR + 1)));
	
	// On réutilise la nouvelle valeur du prescaler pour déterminer la valeur de l'autoreload
	Timer->ARR = (float)((frequence_interne / (frequence * (float)(Timer->PSC + 1))))-1;
	
	return CLOCK_GetTIMCLK(Timer) / (float)( (Timer->PSC + 1)*(Timer->ARR + 1) );
}


/** 
* Configure la routine d'interruption d'un Timer
* @param Timer Pointeur vers le jeu de registres (de type TIM_TypeDef ) du timer considéré
* @param Priority Niveau de priorité de l'interruption
* @param IT_function Pointeur sur la fonction qui sera exécutée dans le routine d'interruption
**/

void Timer_Active_IT( TIM_TypeDef *Timer, u8 Priority, void (*IT_function) (void) )
{
	// activation de l'interruption en mode Update : bit 0 UIE
	Timer->DIER |= 0x01 << 0 ;
	//uint16_t * test = (uint16_t *)(&(Timer->DIER));
	//mise_a_1(Timer->DIER,0);
	
	/*
	if (Timer == TIM1)
	{
		callbackTIM1 = IT_function ;
    NVIC_SetPriority (TIM1_CC_IRQn, Priority );
		NVIC_EnableIRQ (TIM1_CC_IRQn) ;
	}
	*/
	
	if (Timer == TIM2) 
	{
		set_Callback_Timer(2, IT_function) ;
    NVIC_SetPriority (TIM2_IRQn, Priority );
		NVIC_EnableIRQ (TIM2_IRQn) ;
	}
	
	if (Timer == TIM3)
  {	
		set_Callback_Timer(3, IT_function) ;
    NVIC_SetPriority (TIM3_IRQn, Priority );	
	  NVIC_EnableIRQ (TIM3_IRQn) ;
  }
	
	if (Timer == TIM4)
	{	
		set_Callback_Timer(4, IT_function) ;
	  NVIC_SetPriority (TIM4_IRQn, Priority );
	  NVIC_EnableIRQ (TIM4_IRQn) ;
	}
	
}


// PWM (basee sur un des Timers 1, 2, 3, 4 ---------------------------------
// la frequence est en kHz
// la fonction rend la pleine echelle ou resolution, c'est a dire la plage
// de valeurs acceptees pour moduler la largeur d'impulsion
float PWM_Init_Output_Compare( TIM_TypeDef *Timer, char Voie, float frequence) {
	
	/** ************************************************* **/
	//               configuration du Timer
	/** ************************************************* **/
	float duree_us = 1000 / frequence ;
	
	// Activation du Timer s'il n'est pas activé
	if ((Timer == TIM2) && (RCC->APB1ENR & 0x1 << 0) != 0x00000001)
		Timer_1234_Init(Timer, duree_us) ;	
	if ((Timer == TIM3) && (RCC->APB1ENR & 0x1 << 1) != 0x00000002)
		Timer_1234_Init(Timer, duree_us) ;	
	if ((Timer == TIM4) && (RCC->APB1ENR & 0x1 << 2) != 0x00000004)
		Timer_1234_Init(Timer, duree_us) ;	
	if ((Timer == TIM1) && (RCC->APB2ENR & 0x1 << 3) != 0x00000800)
		Timer_1234_Init(Timer, duree_us) ;		
	
	/** ************************************************* **/
	//      configuration de la PWM sur la voie donnee
	/** ************************************************* **/

	// high polarity
	Timer->CCER |= (0x0 << (4*(Voie-1)+1)) ;
	
	switch (Voie) 
	{
		case 1 : 
			// activation du mode 1
			Timer->CCMR1 &= ~(0x7 << 4) ;
			Timer->CCMR1 |= (0x6 << 4) ;
			// activation du preload registre
            Timer->CCMR1 |= (0x1 << 3) ;
		break ;
		case 2 :
			// activation du mode 1
			Timer->CCMR1 &= ~(0x7 << 12) ;
			Timer->CCMR1 |= (0x6 << 12) ;
			// activation du preload registre
			Timer->CCMR1 |= (0x1 << 11) ;
		break ;
		case 3 :
			// activation du mode 1
			Timer->CCMR2 &= ~(0x7 << 4) ;
			Timer->CCMR2 |= (0x6 << 4) ;
			// activation du preload registre
			Timer->CCMR2 |= (0x1 << 3) ;
		break ;
		case 4 :
			// activation du mode 1
			Timer->CCMR2 &= ~(0x7 << 12) ;
			Timer->CCMR2 |= (0x6 << 12) ;
			// activation du preload registre
			Timer->CCMR2 |= (0x1 << 11) ;
    break ;
	}
	
	// activation du auto-reload preload
	Timer->CR1 |= (0x1 << 7) ;
	
  // activation de la PWM sur la voie donnee
	Timer->CCER |= (0x1 << 4*(Voie-1)) ;
	
	return (float)((Timer->ARR)+1) ;
}




// PWM (basee sur un des Timers 1, 2, 3, 4 ---------------------------------
// la voie sur laquelle sera actionnée la PWM
// la frequence est en kHz
void PWM_Init_Input_Capture (TIM_TypeDef *Timer, float frequence) 
{
	// on considère toujours les voies 1 et 2
	char Voie1 = 1 ;
	char Voie2 = 2 ;
	
	/** ************************************************* **/
	//               configuration du Timer
	/** ************************************************* **/
	float duree_us = 1000 / frequence ;
	
	// Activation du Timer s'il n'est pas activé
	if ((Timer == TIM2) && (RCC->APB1ENR & 0x1 << 0) != 0x00000001)
		Timer_1234_Init(Timer, duree_us) ;	
	if ((Timer == TIM3) && (RCC->APB1ENR & 0x1 << 1) != 0x00000002)
		Timer_1234_Init(Timer, duree_us) ;	
	if ((Timer == TIM4) && (RCC->APB1ENR & 0x1 << 2) != 0x00000004)
		Timer_1234_Init(Timer, duree_us) ;	
	if ((Timer == TIM1) && (RCC->APB2ENR & 0x1 << 3) != 0x00000800)
		Timer_1234_Init(Timer, duree_us) ;		
	
	/** ************************************************* **/
	// configuration de la PWM sur la voie donnee du Timer
	/** ************************************************* **/
	
	// high polarity pour Voie1
	Timer->CCER &= ~(0x1 << (4*(Voie1-1)+1)) ;
	// low polarity pour Voie2
	Timer->CCER |= (0x1 << (4*(Voie2-1)+1)) ;
	
	/**      VOIE 1                **/
	// activation du mode input capture
	Timer->CCMR1 &= ~(0x3 << 0) ;
	Timer->CCMR1 |= (0x1 << 0) ;	
	// activation du preload registre
	Timer->CCMR1 |= (0x1 << 3) ;

  /**      VOIE 2                **/
	// activation du mode input capture
	Timer->CCMR1 &= ~(0x3 << 8) ;
	Timer->CCMR1 |= (0x2 << 8) ;
	// activation du preload registre
	Timer->CCMR1 |= (0x1 << 11) ;
	
	// activation du auto-reload preload
	Timer->CR1 |= (0x1 << 7) ;
	
	// Valider le trigger input 
	Timer->SMCR &= ~(0x7 << 4) ;
	Timer->SMCR |= (0x5 << 4) ;
	
	// Activation du slave mode
	Timer->SMCR &= ~(0x7 << 0) ;
	Timer->SMCR |= (0x4 << 0) ;
	
  // activation de la PWM sur la voie donnee
	Timer->CCER |= (0x1 << 4*(Voie1-1)) ;
	Timer->CCER |= (0x1 << 4*(Voie2-1)) ;
}






// PWM (basee sur un des Timers 1, 2, 3, 4 ---------------------------------
// le rapport cyclique en donne en pourcentage
void Set_Duty_Cycle(TIM_TypeDef *Timer, char Voie, float pourcentage)
{
	switch(Voie)
	{
		case(1):
			Timer->CCR1 = (float)(Timer->ARR)*pourcentage/100;
		break;
		case(2):
			Timer->CCR2 = (float)(Timer->ARR)*pourcentage/100;
		break;
		case(3):
			Timer->CCR3 = (float)(Timer->ARR)*pourcentage/100;
		break;
		case(4):
			Timer->CCR4 = (float)(Timer->ARR)*pourcentage/100;
		break;
	}
}



// Fonction qui fait clignoter la diode lors d'une interruption
void TIM1_IRQHandler (void)
{
	if (TIM1->SR &(1<<0))
	{ 
		get_Callback_Timer (1);
	}
		TIM1->SR = TIM1->SR & (~(1<<0)) ;
} 

// Fonction qui fait clignoter la diode lors d'une interruption
void TIM2_IRQHandler (void)
{
	if (TIM2->SR &(1<<0))
	{ 
		(get_Callback_Timer(2))() ;
	}
		TIM2->SR = TIM2->SR & (~(1<<0)) ;
} 

// Fonction qui fait clignoter la diode lors d'une interruption
void TIM3_IRQHandler (void)
{
	if (TIM3->SR &(1<<0))
	{ 
		get_Callback_Timer (3);
	}
		TIM3->SR = TIM3->SR & (~(1<<0)) ;
} 

// Fonction qui fait clignoter la diode lors d'une interruption
void TIM4_IRQHandler (void)
{
	if (TIM4->SR &(1<<0))
	{ 
		get_Callback_Timer (4);
	}
		TIM4->SR = TIM4->SR & (~(1<<0)) ;
} 
